#ifndef VECTOR_H
#define VECTOR_H

namespace cg
{

class Vec3;	
//////////////////////////////////////////////////////////////////////////////
//
//	Vec2
//
///////////////////////////////////////////////////////////////////////////////

class Vec2
{
public:
	float			x;
	float			y;

	Vec2			( void );
	Vec2			( const float x, const float y );

	void			Set( const float x, const float y );

	bool			operator==( const Vec2 &v ) const;
	bool			operator!=( const Vec2 &v ) const;
	Vec2 &			operator=( const Vec2 &v );
	Vec2 &			operator+=( const Vec2 &v );
	Vec2 &			operator/=( float d );
	Vec2 &			operator*=( float m );
	Vec2			operator-( void ) const ;
	Vec2			operator+( const Vec2 &v ) const;
	Vec2			operator-( const Vec2 &v ) const;
	Vec2			operator*( float f ) const;
	float			Len( void ) const;
	float			LenSq( void ) const;
	float			Dist( const Vec2 &v ) const;
	float			DistSq( const Vec2 &v ) const;
	float			Normalize( void );
	Vec3			toVec3( float z = 0 ) const;
	float *			toFloat();
	//
	static const Vec2 ZERO;	
	static const Vec2 X_AXIS;
	static const Vec2 Y_AXIS;
};

CG_INLINE Vec2::Vec2( void )
{

}

CG_INLINE Vec2::Vec2( const float x, const float y )
{
	this->x = x;
	this->y = y;
}

CG_INLINE void Vec2::Set( const float x, const float y )
{
	this->x = x;
	this->y = y;
}

CG_INLINE bool Vec2::operator==( const Vec2 &v ) const
{
	return x == v.x && y == v.y;	
}

CG_INLINE bool Vec2::operator!=( const Vec2 &v ) const
{
	return ! ( ( *this ) == v );	
}

CG_INLINE Vec2 &Vec2::operator=( const Vec2 &v )
{
	x = v.x;
	y = v.y;
	return *this;
}

CG_INLINE Vec2 &Vec2::operator+=( const Vec2 &v )
{
	x += v.x;
	y += v.y;
	return *this;
}

CG_INLINE Vec2 &Vec2::operator*=( float m )
{
	assert( m );
	x *= m;
	y *= m;
	return *this;
}

CG_INLINE Vec2 &Vec2::operator/=( float d )
{
	assert( d );
	x /= d;
	y /= d;
	return *this;
}

CG_INLINE Vec2 Vec2::operator+( const Vec2 &v ) const
{
	return Vec2( x + v.x, y + v.y );
}

CG_INLINE Vec2 Vec2::operator-( const Vec2 &v ) const
{
	return (*this) + ( -v );
}

CG_INLINE Vec2 Vec2::operator-( void ) const 
{
	return Vec2( -x, -y );
}

CG_INLINE Vec2 Vec2::operator*( float f ) const 
{
	return Vec2( x * f, y * f );
}

CG_INLINE float Vec2::Len( void ) const
{
	return sqrt( x * x + y * y );
}

CG_INLINE float Vec2::Dist( const Vec2 &v ) const
{
	float tx = v.x - x;
	float ty = v.y - y;	
	
	return sqrt( tx * tx + ty * ty );
}

CG_INLINE float Vec2::Normalize( void )
{
	float sqLength, invLength;

	sqLength = x * x + y * y;
	assert( sqLength != 0 );
	invLength = 1.0f / sqrt( sqLength );

	x *= invLength;
	y *= invLength;
	return invLength * sqLength;
}

CG_INLINE float	Vec2::LenSq( void ) const
{
	return x*x + y*y;
}

CG_INLINE float Vec2::DistSq( const Vec2 &v ) const
{
	float tx = v.x - x;
	float ty = v.y - y;	
	return tx * tx + ty * ty;
}

CG_INLINE float * Vec2::toFloat()
{
	return &x;
}

CG_INLINE Vec2 Perpendicular( const Vec2 &v )
{
	return Vec2( -v.y, v.x );
}

CG_INLINE float Determinant( const Vec2 &A, const Vec2 &B, const Vec2 &X )
{
	Vec2 v1 = X - A;
	Vec2 v2 = X - B;
	return v1.x * v2.y - v1.y * v2.x;
}

///////////////////////////////////////////////////////////////////////////////
//
//	Vec3
//
///////////////////////////////////////////////////////////////////////////////

class Vec3
{
public:
	float			x;
	float			y;
	float			z;

	Vec3			( void );
	Vec3			( const float x, const float y, const float z );
	Vec3			( float *v );
	
	void			Set( const float x, const float y, const float z );


	Vec3&			operator=( const Vec3 &v );
	Vec3			operator-( void ) const ;
	bool			operator==( const Vec3 &v ) const;
	bool			operator!=( const Vec3 &v ) const;	
	Vec3 &			operator+=( const Vec3 &v );
	Vec3 &			operator/=( float d );
	Vec3 &			operator*=( float m );	
	Vec3			operator+( const Vec3 &v ) const;
	Vec3			operator-( const Vec3 &v ) const;
	Vec3			operator*( float f ) const;	
	//
	float			Len( void ) const;
	float			LenSq( void ) const;
	float			Dist( const Vec3 &v ) const;
	float			DistSq( const Vec3 &v ) const;
	float			Normalize( void );
	float			DotProduct( const Vec3 &v ) const;
	Vec3			Cross( const Vec3 &b ) const;
	//
	float*			toFloat( void );
	//
	static const Vec3 ZERO;
	static const Vec3 X_AXIS;
	static const Vec3 Y_AXIS;
	static const Vec3 Z_AXIS;
};

CG_INLINE Vec3::Vec3( void )
{

}

CG_INLINE Vec3::Vec3( const float x, const float y, const float z )
{
	this->x = x;
	this->y = y;
	this->z = z;
}

CG_INLINE Vec3::Vec3( float *v )
{
	assert( v );
	this->x = v[ 0 ];
	this->y = v[ 1 ];
	this->z = v[ 2 ];
}

CG_INLINE void Vec3::Set( const float x, const float y, const float z )
{
	this->x = x;
	this->y = y;
	this->z = z;
}

CG_INLINE Vec3 &Vec3::operator=( const Vec3 &v )
{
	x = v.x;
	y = v.y;
	z = v.z;
	return *this;
}

CG_INLINE Vec3 Vec3::operator-( void ) const 
{
	return Vec3( -x, -y, -z );
}

CG_INLINE bool Vec3::operator==( const Vec3 &v ) const
{
	return x == v.x && y == v.y && z == v.z;
}

CG_INLINE bool Vec3::operator!=( const Vec3 &v ) const
{
	return ! ( ( *this ) == v );	
}

CG_INLINE Vec3 &Vec3::operator+=( const Vec3 &v )
{
	x += v.x;
	y += v.y;
	z += v.z;
	return *this;
}

CG_INLINE Vec3 &Vec3::operator*=( float m )
{
	assert( m );
	x *= m;
	y *= m;
	z *= m;
	return *this;
}

CG_INLINE Vec3 &Vec3::operator/=( float d )
{
	assert( d );
	x /= d;
	y /= d;
	z /= d;
	return *this;
}

CG_INLINE Vec3 Vec3::operator+( const Vec3 &v ) const
{
	return Vec3( x + v.x, y + v.y, z + v.z );
}

CG_INLINE Vec3 Vec3::operator-( const Vec3 &v ) const
{
	return (*this) + ( -v );
}

CG_INLINE Vec3 Vec3::operator*( float f ) const 
{
	return Vec3( x * f, y * f, z * f );
}

CG_INLINE float Vec3::Len( void ) const
{
	return sqrt( x * x + y * y + z * z);
}

CG_INLINE float Vec3::Dist( const Vec3 &v ) const
{
	float tx = v.x - x;
	float ty = v.y - y;	
	float tz = v.y - z;	
	
	return sqrt( tx * tx + ty * ty + tz * tz );
}

CG_INLINE float Vec3::Normalize( void )
{
	float sqLength, invLength;

	sqLength = x * x + y * y + z * z;
	assert( sqLength != 0 );	
	invLength = 1.0f / sqrt( sqLength );

	x *= invLength;
	y *= invLength;
	z *= invLength;
	return invLength * sqLength;
}

CG_INLINE float	Vec3::LenSq( void ) const
{
	return x*x + y*y + z*z;
}

CG_INLINE float Vec3::DistSq( const Vec3 &v ) const
{
	float tx = v.x - x;
	float ty = v.y - y;	
	float tz = v.z - z;	
	return tx * tx + ty * ty + tz * tz;
}

CG_INLINE float Vec3::DotProduct( const Vec3 &v ) const
{
	return x * v.x + y * v.y + z * v.z;
}

CG_INLINE Vec3 Vec3::Cross( const Vec3 &b ) const
{
	return Vec3( y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x );
}

CG_INLINE float * Vec3::toFloat()
{
	return &x;
}

CG_INLINE Vec3 Cross( const Vec3 &a, const Vec3 &b )
{
	return Vec3( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x );
}

///////////////////////////////////////////////////////////////////////////////
//
//	Vec4
//
///////////////////////////////////////////////////////////////////////////////

class Vec4
{
public:
	float			x;
	float			y;
	float			z;
	float			w;

	Vec4			( void );
	Vec4			( const float v );
	Vec4			( const float v, const float alpha );
	Vec4			( const float x, const float y, const float z, const float w );

	void			Set( const float x, const float y, const float z, const float w );

	Vec4&			operator=( const Vec4 &v );	

	Vec4			operator-( void ) const ;
	Vec4 			operator*( float m ) const;	
	Vec4 			operator+( const Vec4 &v ) const;
	Vec4 			operator-( const Vec4 &v ) const;
	Vec4 			operator*( const Vec4 &v ) const;
	bool			operator==( const Vec4 &v ) const;
	bool			operator!=( const Vec4 &v ) const;		
	Vec4 &			operator+=( const Vec4 &v );	
	Vec4 &			operator/=( float d );
	Vec4 &			operator*=( float m );	

	float			DotProduct( const Vec4 &v ) const;
	float			Normalize( void );

	float*			toFloat( void );
	const float*	toFloat( void ) const;
	Vec3			toVec3( void ) const;

	static const Vec4 ZERO;
	static const Vec4 X_AXIS;
	static const Vec4 Y_AXIS;
	static const Vec4 Z_AXIS;	
	static const Vec4 W_AXIS;
};

CG_INLINE Vec4::Vec4()
{

}

CG_INLINE Vec4::Vec4( const float v )
{
	this->x = v;
	this->y = v;
	this->z = v;
	this->w = v;
}

CG_INLINE Vec4::Vec4( const float v, const float alpha )
{
	this->x = v;
	this->y = v;
	this->z = v;
	this->w = alpha;
}

CG_INLINE Vec4::Vec4( const float x, const float y, const float z, const float w )
{
	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}

CG_INLINE void Vec4::Set( const float x, const float y, const float z, const float w )
{
	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}

CG_INLINE Vec4 &Vec4::operator=( const Vec4 &v )
{
	x = v.x;
	y = v.y;
	z = v.z;
	w = v.w;
	return *this;
}

CG_INLINE Vec4 Vec4::operator-( void ) const
{
	return Vec4( -x, -y, -z, -w );
}

CG_INLINE bool Vec4::operator==( const Vec4 &v ) const
{
	return x == v.x && y == v.y && z == v.z && w == v.w;
}

CG_INLINE bool Vec4::operator!=( const Vec4 &v ) const
{
	return ! ( ( *this ) == v );	
}

CG_INLINE Vec4 &Vec4::operator+=( const Vec4 &v )
{
	x += v.x;
	y += v.y;
	z += v.z;
	w += v.w;
	return *this;
}

CG_INLINE Vec4 &Vec4::operator*=( float m )
{	
	x *= m;
	y *= m;
	z *= m;
	w *= m;
	return *this;
}

CG_INLINE Vec4 &Vec4::operator/=( float d )
{
	assert( d );
	x /= d;
	y /= d;
	z /= d;
	w /= d;
	return *this;
}

CG_INLINE Vec4 Vec4::operator+( const Vec4 &v ) const
{
	return Vec4( x + v.x, y + v.y, z + v.z, w + v.w );
}

CG_INLINE Vec4 Vec4::operator-( const Vec4 &v ) const
{
	return (*this) + ( -v );
}

CG_INLINE Vec4 Vec4::operator*( float f ) const 
{
	return Vec4( x * f, y * f, z * f, w * f );
}

CG_INLINE float Vec4::DotProduct( const Vec4 &v ) const
{
	return x * v.x + y * v.y + z * v.z + w * v.w;
}

CG_INLINE float Vec4::Normalize( void )
{
	float sqLength, invLength;

	sqLength = x * x + y * y + z * z + w * w;
	assert( sqLength != 0 );
	invLength = 1.0f / sqrt( sqLength );

	x *= invLength;
	y *= invLength;
	z *= invLength;
	w *= invLength;
	return invLength * sqLength;
}

CG_INLINE float* Vec4::toFloat( void )
{
	return &x;
}


CG_INLINE const float* Vec4::toFloat( void ) const
{
	return &x;
}

CG_INLINE Vec3 Vec4::toVec3( void ) const
{
	if ( w != 0 )
		return Vec3( x / w, y / w, z / w );
	else
		return Vec3( x, y, z );
}

// misc

CG_INLINE Vec4 toVec4( const Vec3& v, float w = 0 )
{
	return Vec4( v.x, v.y, v.z, w );
}

CG_INLINE Vec3 Vec2::toVec3( float z ) const
{
	return Vec3( x, y, z );
}

}

#endif